﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using Newtonsoft.Json.Linq;
using System.Diagnostics;

namespace Core.Rules
{
    public abstract class PositiveWords : IRule
    {
        public abstract double apply(SiteEnum site, QuestionInformation info);

        /// <summary>
        /// This is taken from: 
        /// http://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986
        /// 
        /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
        /// </summary>
        internal static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };

        /// <summary>
        /// This is taken from: 
        /// http://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986
        /// 
        /// Escapes a string according to the URI data string rules given in RFC 3986.
        /// </summary>
        /// <param name="value">The value to escape.</param>
        /// <returns>The escaped value.</returns>
        /// <remarks>
        /// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
        /// RFC 3986 behavior if certain elements are present in a .config file.  Even if this
        /// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
        /// host actually having this configuration element present.
        /// </remarks>
        internal static string EscapeUriDataStringRfc3986(string value)
        {
            // Start with RFC 2396 escaping by calling the .NET method to do the work.
            // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
            // If it does, the escaping we do that follows it will be a no-op since the
            // characters we search for to replace can't possibly exist in the string.
            StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));

            // Upgrade the escaping to RFC 3986, if necessary.
            for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
            {
                escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
            }

            // Return the fully-RFC3986-escaped string.
            return escaped.ToString();
        }

        abstract public string RuleName { get; }

        /// <summary>
        /// Execute Alchemy API and return value for an input string
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static double ExecuteAlchemyAPI(string text)
        {
            //Use the sentiment analysis API
            HttpWebRequest req = WebRequest.Create(new Uri("http://access.alchemyapi.com/calls/text/TextGetTextSentiment")) as HttpWebRequest;
            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";

            string parameters = "apikey=7cb73069c0bd71d96364976e2920ac7f20bd5526&outputMode=json&";
            parameters += "text=" + EscapeUriDataStringRfc3986(text);

            // Encode the parameters as form data:
            byte[] formData = UTF8Encoding.UTF8.GetBytes(parameters.ToString());
            req.ContentLength = formData.Length;

            // Send the request:
            using (Stream post = req.GetRequestStream())
            {
                post.Write(formData, 0, formData.Length);
            }

            // Pick up the response:
            string result = null;
            using (HttpWebResponse resp = req.GetResponse() as HttpWebResponse)
            {
                StreamReader reader = new StreamReader(resp.GetResponseStream());
                result = reader.ReadToEnd();
            }

            //Parse the result to get the score
            JObject j = JObject.Parse(result);
            double sentiment = 0;
            try
            {
                if (j["docSentiment"]["type"].ToString() == "neutral") return 0;
                sentiment = Convert.ToDouble(j["docSentiment"]["score"].ToString());
            }
            catch (Exception e)
            {
                //Do nothing, we overran the daily limit...
                Trace.WriteLine("Overran the Alchemy API daily limit");
            }
            return sentiment;
        }
    }
}
